<template>
    <a-dropdown :popPlacement="props.popPlacement" :useTargetWidth="true" :maxHeight="props.maxHeight" class="a-input-tree-multi" :class="[`color-${formItemHook.myColor}`]" v-model:popShown="state.popShown" :trigger="'none'">
        <a-control-group class="a-input-tree-multi">
            <a-input-string class="c-w-100" :color="formItemHook.myColor" :size="formItemHook.mySize" :sharp="formItemHook.mySharp" :disabled="formItemHook.myDisabled" :value="displayText" :placeholder="props.placeholder" :readonly="true" :clearable="props.clearable" :isInner="true" @click="state.popShown = !state.popShown" @clear="methods.clear"></a-input-string>
            <a-button v-if="props.attachButtonShown" class="c-f-shrink-0" :color="formItemHook.myColor" :size="formItemHook.mySize" :sharp="formItemHook.mySharp" :disabled="formItemHook.myDisabled" :square="true" ref="btn" @click="state.popShown = !state.popShown"><span class="fa fa-fw fa-angle-down"></span></a-button></a-control-group>

        <template v-slot:pop>
            <a-tree :items="props.items" :getItemsFunc="methods.myGetItemsFunc" :getKeyFunc="props.getTextFunc" :getIsFolderFunc="props.getIsFolderFunc" :getIsItemOpenedFunc="methods.isItemOpened" class="pop" :class="[
                !formItemHook.mySharp ? `c-round-${formItemHook.mySize}` : '',
                ` c-shadow-${formItemHook.mySize}`,
            ]">
                <template v-slot:foldName="{
                    item,
                    parent,
                    depth,
                    index,
                    itemCount,
                    opened,
                }">
                    <div class="c-f-h c-f-start-center c-w-100">
                        <template v-if="parentPathMapKeyByItem.get(item)">
                            <a-line-header v-for="(_p, i) in parentPathMapKeyByItem.get(
                                item
                            )" :color="formItemHook.myColor" :top="parentPathMapKeyByItem.get(item)[i + 1] ==
    undefined ||
    !isLastItemSet.has(
        parentPathMapKeyByItem.get(item)[i + 1]
    )
    " :right="i ===
        parentPathMapKeyByItem.get(item).length - 1
        " :bottom="!isLastItemSet.has(
        parentPathMapKeyByItem.get(item)[
        i + 1
        ] ?? item
    )
        " class="c-f-shrink-0"></a-line-header>
                        </template>
                        <a-control-group :block="true" class="c-f-grow-1">
                            <a-button :color="formItemHook.myColor" :size="formItemHook.mySize" :sharp="true" :plain="true" :square="true" @click="methods.toggleOpenItem(item)" class="c-f-shrink-0 c-position-relative">
                                <span class="fa fa-fw fa-folder" v-if="!opened"></span>
                                <span class="fa fa-fw fa-folder-open" v-else></span>
                                <span class="dot" v-if="selectedPathSet.has(item)"></span>
                            </a-button>
                            <a-input-check-box-single v-if="props.canSelectFolder" :color="formItemHook.myColor" :size="formItemHook.mySize" :sharp="true" :disabled="methods.itemDisabled(item)" :block="true" :plain="true" :value="selectedItemSet.has(item)" :isInner="true" @click="methods.clickItem(item)" class="c-f-h c-f-between-center">
                                <template v-slot:prepend>
                                    <span>{{ props.getTextFunc(item) }}</span>
                                </template>
                            </a-input-check-box-single>
                            <div v-else class="c-f-h c-f-between-center c-cursor-default c-w-100" :class="[
                                `c-height-${formItemHook.mySize}`,
                                `c-font-size-${formItemHook.mySize}`,
                                `c-front-color-${formItemHook.myColor}-reverse`,
                                `c-p-h-${formItemHook.mySize}`,
                            ]">
                                <span>{{ props.getTextFunc(item) }}</span>
                                <span class="fa fa-fw fa-check" v-if="selectedItemSet.has(item)"></span>
                            </div>
                        </a-control-group>
                    </div>
                </template>
                <template v-slot:file="{ item, parent, depth, index, itemCount }">
                    <div class="c-f-h c-f-start-center c-w-100">
                        <template v-if="parentPathMapKeyByItem.get(item)">
                            <a-line-header v-for="(_p, i) in parentPathMapKeyByItem.get(
                                item
                            )" :color="formItemHook.myColor" :top="parentPathMapKeyByItem.get(item)[i + 1] ==
    undefined ||
    !isLastItemSet.has(
        parentPathMapKeyByItem.get(item)[i + 1]
    )
    " :right="i ===
        parentPathMapKeyByItem.get(item).length - 1
        " :bottom="!isLastItemSet.has(
        parentPathMapKeyByItem.get(item)[
        i + 1
        ] ?? item
    )
        " class="c-f-shrink-0"></a-line-header>
                        </template>
                        <div class="c-f-h c-f-center-center c-f-shrink-0" :class="[
                            `c-square-${formItemHook.mySize}`,
                            `c-front-color-${formItemHook.myColor}-reverse`,
                        ]">
                            <span class="fa fa-fw fa-file"></span>
                        </div>
                        <a-input-check-box-single :color="formItemHook.myColor" :size="formItemHook.mySize" :sharp="true" :disabled="methods.itemDisabled(item)" :block="true" :plain="true" :value="selectedItemSet.has(item)" :isInner="true" @click="methods.clickItem(item)" class="c-f-h c-f-between-center">
                            <template v-slot:prepend>
                                <span>{{ props.getTextFunc(item) }}</span>
                            </template>
                        </a-input-check-box-single>
                    </div>
                </template>
            </a-tree>
        </template>
    </a-dropdown>
</template>

<style lang="scss" scoped>
@import "../scss/vars";

.pop {
    background-color: $defaultBgColor;
}

.dot {
    background-color: #ff0000;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    position: absolute;
    right: 0;
    top: 0;
}
</style>

<script setup lang="ts">
import AInputString from './AInputString.vue'
import AButton from './AButton.vue'
import AControlGroup from './AControlGroup.vue'
import ADropdown from './ADropdown.vue'
import ATree from './ATree.vue'
import ALineHeader from './ALineHeader.vue'
import AInputCheckBoxSingle from './AInputCheckBoxSingle.vue'
import { InputPropsType, PopPlacementType, Rule_maxLength, Rule_minLength } from '@/types'
import { computed, reactive } from 'vue'
import { useInput } from '@/hooks/useInput'
import { useFormItem } from '@/hooks/useFormItem'
import { $t } from '@/lang'

interface PropsType extends InputPropsType<any[] | undefined, Rule_minLength | Rule_maxLength> {
    getDisabledFunc: (a: any) => boolean,
    items: any[]
    getItemsFunc?: (a: any) => any[]
    getIsFolderFunc?: (a: any) => boolean
    getTextFunc?: (a: any) => string
    separator?: string
    useShortDisplayText?: boolean
    getValueFunc?: (a: any) => any
    popPlacement?: PopPlacementType
    canSelectFolder?: boolean
    clearable?: boolean
    maxHeight?: number
    placeholder?: string
    attachButtonShown?: boolean
}
const props = withDefaults(defineProps<PropsType>(), {
    value: undefined,
    color: undefined,
    disabled: undefined,
    size: undefined,
    sharp: undefined,
    rules: () => [],
    isInner: false,
    getDisabledFunc: (a: any) => a.disabled,
    items: () => [],
    getItemsFunc: (a: any) => a.items,
    getIsFolderFunc: (a: any) => a.type === 0,
    getTextFunc: (a: any) => a.text,
    separator: ' ',
    useShortDisplayText: false,
    getValueFunc: (a: any) => a.value,
    popPlacement: 'bottom',
    canSelectFolder: false,
    clearable: true,
    maxHeight: undefined,
    placeholder: undefined,
    attachButtonShown: true,
})

interface StateType {
    popShown: boolean
    openedItemSet: Set<any>
}
const state = reactive<StateType>({
    popShown: false,
    openedItemSet: new Set()
})

const emit = defineEmits<{
    (e: 'update:value', value?: any[]): void
}>()
useInput(props)
const formItemHook = useFormItem(props)

const displayText = computed(() => {
    let selectedItems = selectedItemList.value
    if (selectedItems.length === 0) {
        return ''
    }
    if (props.useShortDisplayText) {
        return props.getTextFunc(selectedItems[0]) + (selectedItems.length > 1 ? $t('AInputTreeMulti.ItemsIntotal', [selectedItems.length]) : '')
    } else {
        return selectedItems.map(a => props.getTextFunc(a)).join(props.separator)
    }
})
const selectedItemList = computed(() => {
    let selectedItems = (props.value ?? []).map(a => methods.findSelectedItem(props.items, a)).filter(a => a)
    return selectedItems
})
const selectedItemSet = computed(() => {
    return new Set(selectedItemList.value)
})
const selectedPathList = computed(() => {
    let list: any[] = []
    selectedItemList.value.forEach(item => {
        let selectedPathList = [item]
        let parent = parentMapKeyByItem.value.get(item)
        while (parent) {
            selectedPathList.unshift(parent)
            parent = parentMapKeyByItem.value.get(parent)
        }
        list = [...list, ...selectedPathList]
    })
    return Array.from(new Set(list))
})
const selectedPathSet = computed(() => {
    return new Set(selectedPathList.value)
})
const itemsMapKeyByParent = computed(() => {
    let map = new Map<any, any[]>()
    let toBeComputedItems = [...props.items]
    while (toBeComputedItems.length) {
        let toBeComputedItem = toBeComputedItems.shift()
        if (props.getIsFolderFunc(toBeComputedItem)) {
            let subItems = props.getItemsFunc(toBeComputedItem)
            map.set(toBeComputedItem, subItems)
            toBeComputedItems = [...toBeComputedItems, ...subItems]
        }
    }
    return map
})
const parentMapKeyByItem = computed(() => {
    let map = new Map()
    itemsMapKeyByParent.value.forEach((items, parent) => {
        items.forEach(a => map.set(a, parent))
    })
    return map
})
const parentPathMapKeyByItem = computed(() => {
    let map = new Map()
    parentMapKeyByItem.value.forEach((parent, item) => {
        let parentPath = []
        let _p = parent
        while (_p) {
            parentPath.unshift(_p)
            _p = parentMapKeyByItem.value.get(_p)
        }
        map.set(item, parentPath)
    })
    return map
})
const isLastItemSet = computed(() => {
    let set = new Set()
    itemsMapKeyByParent.value.forEach(items => {
        let lastItem = items[items.length - 1]
        if (lastItem) {
            set.add(lastItem)
        }
    })
    return set
})

const methods = {
    input(val?: any[]) {
        emit('update:value', val)
    },
    clear() {
        methods.input([])
    },
    clickItem(item: any[]) {
        let itemValue = props.getValueFunc(item)
        if (selectedItemSet.value.has(item)) {
            methods.input((props.value ?? []).filter(a => a !== itemValue))
        } else {
            methods.input([...(props.value ?? []), itemValue])
        }
    },
    toggleOpenItem(item: any) {
        if (methods.isItemOpened(item)) {
            state.openedItemSet.delete(item)
        } else {
            state.openedItemSet.add(item)
        }
    },
    isItemOpened(item: any) {
        return state.openedItemSet.has(item)
    },
    findSelectedItem(items: any[], value: any): any {
        for (let i = 0; i < items.length; i++) {
            let item = items[i]
            if (props.getValueFunc(item) === value) {
                return item
            }
            if (props.getIsFolderFunc(item)) {
                let itemInSubDir = methods.findSelectedItem(methods.myGetItemsFunc(item), value)
                if (itemInSubDir !== undefined) {
                    return itemInSubDir
                }
            }
        }
        return undefined
    },
    itemDisabled(item: any) {
        if (formItemHook.value.myDisabled === true) {
            return true
        }
        if (props.getDisabledFunc(item)) {
            return true
        }
        let parent = parentMapKeyByItem.value.get(item)
        if (parent !== undefined && methods.itemDisabled(parent)) {
            return true
        }
        return false
    },
    myGetItemsFunc(item: any) {
        return itemsMapKeyByParent.value.get(item)!
    },
    reset() {
        state.openedItemSet = new Set()
    },
}
</script>